#include "../include/Snake.hpp"
#include "../include/Utils.hpp"

using namespace std;

int SnakeBase::_Collision[MAP_SIZE_X][MAP_SIZE_Y] = {0};
int SnakeBase::_Type[MAP_SIZE_X][MAP_SIZE_Y] = {0};
int SnakeBase::_Updated = 0;
int SnakeBase::_counter = 0;

Snake::Snake()
{
    alive = true;
    id = ++cnt;
    _id = ++_counter;
    _direction = SnakeList[_id - 1]._direction;
    score = 0;
    alive = true;
    for (int i = 0; i < 3; i++)
    {
        Body.push_back(SnakeList[_id - 1].Body[i]);
    }
    for (deque<POS>::iterator i = Body.begin(); i != Body.end(); i++)
    {
        _Collision[i->x][i->y] |= 1 << (_id - 1);
        if (i == Body.begin())
            _Type[i->x][i->y] |= 1 << (_id - 1);
    }
}

int Snake::length() const
{
    return Body.size();
}

void Snake::draw()
{
    MAP &mmap = MAP::Singleton();
    for (deque<POS>::iterator i = Body.begin(); i != Body.end(); i++)
    {
        if (i == Body.begin())
            mmap.putItem(*i, NODE_TYPE::SNAKE_HEAD, id);
        else
            mmap.putItem(*i, NODE_TYPE::SNAKE_BODY, id);
    }
}

bool Snake::turn_check(enum Orientation _dir)
{
    if (_dir == WEST)
        return _direction != 2;
    else if (_dir == EAST)
        return _direction != 0;
    else if (_dir == NORTH)
        return _direction != 1;
    else
        return _direction != 3;
}

void Snake::turn_to(enum Direction _dir)
{
    if (_dir == LEFT)
    {
        _direction++;
        if (_direction > 3)
            _direction -= 4;
    }
    else if (_dir == RIGHT)
    {
        _direction--;
        if (_direction < 0)
            _direction += 4;
    }
}

void Snake::turn_to(enum Orientation _dir)
{
    if (!turn_check(_dir) || _dir == _direction)
        return;
    _direction = (int)_dir;
}

int Snake::get_direction()
{
    return _direction;
}

void Snake::update()
{
    _Updated |= 1 << (_id - 1);
    MAP &mmap = MAP::Singleton();
    POS nowhead = Body.front();
    POS newpos = nowhead + DIR[_direction];
    Body.push_front(newpos);
    _Collision[newpos.x][newpos.y] |= 1 << (_id - 1);
    Body.pop_back();
}

void Snake::kill()
{

}

int Snake::getscore()
{

}

SnakeOperation &SnakeOperation::Singleton()
{
    static SnakeOperation singleton;
    return singleton;
}

shared_ptr<Snake> SnakeOperation::CreateSnake()
{
    shared_ptr<Snake> _snk = shared_ptr<Snake>(new Snake);
    __snakes.push_back(_snk);
    return _snk;
}

void SnakeOperation::update()
{
    for (vector<shared_ptr<Snake>>::iterator i = __snakes.begin(); i != __snakes.end(); i++)
    {
        (*i)->update();
    }
}

bool SnakeOperation::Snake_Comparer(const Snake &a, const Snake &b)
{
    int id1 = a._id,id2 = b._id;
    if((SnakeBase::_Updated & (1<<id1)) && !(SnakeBase::_Updated & (1<<id2))) return true;
    else if(!(SnakeBase::_Updated & (1<<id1)) && (SnakeBase::_Updated & (1<<id2))) return false;
    else if (!(SnakeBase::_Updated & (1 << id1)) && !(SnakeBase::_Updated & (1 << id2)))
        return __snakes[id1]->length() < __snakes[id2]->length();
    else
    {
        
    }
}

void SnakeOperation::lateUpdate()
{
    if (!SnakeBase::_Updated)
        return;
    MAP &mmap = MAP::Singleton();
    for (vector<shared_ptr<Snake>>::iterator i = __snakes.begin(); i != __snakes.end(); i++)
    {
        shared_ptr<Snake> _now = *i;
        POS _hpos = _now->Body.front();
        if (mmap.getNodeType(_hpos) == NODE_TYPE::FRUIT)
        {
            int _fruitid = mmap.getSubID(_hpos);
            _now->score += __fruits[_fruitid]->getscore();
            __fruits[_fruitid]->eat();
        }
    }
    for (int i = 0; i < MAP_SIZE_X; i++)
    {
        for (int j = 0; j < MAP_SIZE_Y; j++)
        {
            vector<shared_ptr<Snake>> CollisionList;
            bool Unupdated_Exist = false, Body_Exist = false;
            shared_ptr<Snake> Unupdated_One, Body_One;
            int Unupdated_id, Body_id;
            for (int k = 0; k < SnakeBase::_counter; k++)
            {
                CollisionList.push_back(__snakes[i]);
            }
            int (*TP)[25] = SnakeBase::_Type;
            sort(CollisionList.begin(), CollisionList.end(), [=](const Snake &a, const Snake &b) -> bool
                 {
                     int id1 = a._id-1, id2 = b._id-1;
                     if ((SnakeBase::_Updated & (1 << id1)) && !(SnakeBase::_Updated & (1 << id2)))
                         return true;
                     else if (!(SnakeBase::_Updated & (1 << id1)) && (SnakeBase::_Updated & (1 << id2)))
                         return false;
                     else if (!(SnakeBase::_Updated & (1 << id1)) && !(SnakeBase::_Updated & (1 << id2)))
                        { if( __snakes[id1]->length() < __snakes[id2]->length()) return true;
                            else if (__snakes[id1]->length() > __snakes[id2]->length()) return false;
                            else
                                return Random.randBit();
                        }
                     else
                     {
                         if ((TP[i][j] & (1 << id1)) == 0 && (TP[i][j] & (1 << id2)) != 0)
                             return false;
                         else if ((TP[i][j] & (1 << id1)) != 0 && (TP[i][j] & (1 << id2)) == 0)
                             return true;
                         else
                         {
                             if (__snakes[id1]->length() < __snakes[id2]->length())
                                 return true;
                             else if (__snakes[id1]->length() > __snakes[id2]->length())
                                 return false;
                             else
                                 return Random.randBit();
                         }
                     }
                 });
            for(int k=1;k<(int)CollisionList.size();k++)
                ;
        }
    }
}

/**
 * 多蛇碰撞：
 * 一级分类：未更新>更新
 * 均更新：蛇身>蛇头
 * 若相同则比较长度。
 * 长度相同则。。。累了，随便死一个吧